/*
  * Copyright (c) 2016 MediaTek Inc.  All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
  * General Public License (GPL) Version 2, available from the file
  * COPYING in the main directory of this source tree, or the
  * BSD license below:
  *
  *     Redistribution and use in source and binary forms, with or
  *     without modification, are permitted provided that the following
  *     conditions are met:
  *
  *      - Redistributions of source code must retain the above
  *        copyright notice, this list of conditions and the following
  *        disclaimer.
  *
  *      - Redistributions in binary form must reproduce the above
  *        copyright notice, this list of conditions and the following
  *        disclaimer in the documentation and/or other materials
  *        provided with the distribution.
  *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */


#include    "rt_config.h"

#ifdef WSC_V2_SUPPORT
#include    "wsc_tlv.h"

BOOLEAN	WscAppendV2SubItem(
	IN	UCHAR			SubID,
	IN	PUCHAR			pData,
	IN	UCHAR			DataLen,
	OUT	PUCHAR			pOutBuf,
	OUT	PUCHAR			pOutBufLen);

#ifdef CONFIG_AP_SUPPORT
VOID 	WscOnOff(
	IN  PRTMP_ADAPTER	pAd,
	IN  INT				ApIdx,
	IN  BOOLEAN			bOff)
{
	PWSC_V2_INFO	pWpsV2Info = &pAd->ApCfg.MBSSID[ApIdx & 0x0F].WscControl.WscV2Info;
	if (bOff)
	{
		/*
			AP must not support WEP in WPS V2
		*/
		pWpsV2Info->bWpsEnable = FALSE;
		pAd->ApCfg.MBSSID[ApIdx & 0x0F].WscIEBeacon.ValueLen = 0;
		pAd->ApCfg.MBSSID[ApIdx & 0x0F].WscIEProbeResp.ValueLen = 0;
		DBGPRINT(RT_DEBUG_TRACE, ("WscOnOff - OFF.\n"));
	}
	else
	{
		pWpsV2Info->bWpsEnable = TRUE;
		if (pAd->ApCfg.MBSSID[ApIdx & 0x0F].WscControl.WscConfMode != WSC_DISABLE)
		{
			INT IsAPConfigured;
			IsAPConfigured = pAd->ApCfg.MBSSID[ApIdx & 0x0F].WscControl.WscConfStatus;
			WscBuildBeaconIE(pAd, IsAPConfigured, FALSE, 0, 0, (ApIdx & 0x0F), NULL, 0, AP_MODE);
			WscBuildProbeRespIE(pAd, WSC_MSGTYPE_AP_WLAN_MGR, IsAPConfigured, FALSE, 0, 0, ApIdx, NULL, 0, AP_MODE);
			DBGPRINT(RT_DEBUG_TRACE, ("WscOnOff - ON.\n"));
		}
	}
	DBGPRINT(RT_DEBUG_TRACE, ("WscOnOff - bWpsEnable = %d\n", pWpsV2Info->bWpsEnable));
}

VOID	WscAddEntryToAclList(
	IN  PRTMP_ADAPTER	pAd,
	IN	INT				ApIdx,
	IN  PUCHAR			pMacAddr)
{
	PRT_802_11_ACL	pACL = NULL;
	INT				i;
	BOOLEAN			bFound = FALSE;
	
	pACL = &pAd->ApCfg.MBSSID[ApIdx].AccessControlList;

	if ((pACL->Policy == 0) ||
		(pACL->Policy == 2))
		return;
	
	if (pACL->Num >= (MAX_NUM_OF_ACL_LIST - 1))
    {
		DBGPRINT(RT_DEBUG_WARN, ("The AccessControlList is full, and no more entry can join the list!\n"));		
		return;
	}

	for (i=0; i<pACL->Num; i++)
	{
		if (NdisEqualMemory(pACL->Entry[i].Addr, pMacAddr, MAC_ADDR_LEN))
			bFound = TRUE;
	}
	if (bFound == FALSE)
	{
		NdisMoveMemory(pACL->Entry[i].Addr, pMacAddr, MAC_ADDR_LEN);
		pACL->Num++;
	}
}

VOID WscSetupLockTimeout(
	IN PVOID SystemSpecific1,
	IN PVOID FunctionContext,
	IN PVOID SystemSpecific2,
	IN PVOID SystemSpecific3)
{
	PWSC_CTRL 		pWscControl = (PWSC_CTRL)FunctionContext;
	PRTMP_ADAPTER 	pAd = NULL;

	if (pWscControl == NULL)
		return;

	pAd = (PRTMP_ADAPTER)pWscControl->pAd;

	if (pAd == NULL)
		return;

	pWscControl->bSetupLock = FALSE;
	pWscControl->WscSetupLockTimerRunning = FALSE;

		WscBuildBeaconIE(pAd,
						 pWscControl->WscConfStatus,
						 FALSE,
						 0,
						 0,
						 (pWscControl->EntryIfIdx & 0xF),
						 NULL,
						 0,
						 AP_MODE);
		WscBuildProbeRespIE(pAd,
							WSC_MSGTYPE_AP_WLAN_MGR,
							pWscControl->WscConfStatus,
							FALSE,
							0,
							0,
							pWscControl->EntryIfIdx,
							NULL,
							0,
							AP_MODE);

	APUpdateBeaconFrame(pAd, pWscControl->EntryIfIdx & 0x0F);
	DBGPRINT(RT_DEBUG_TRACE, ("WscSetupLockTimeout!\n"));

	return;
}


VOID	WscCheckPinAttackCount(
	IN  PRTMP_ADAPTER	pAd,
	IN  PWSC_CTRL		pWscControl)
{
	BOOLEAN	bCancelled;

	if ((pWscControl->EntryIfIdx & MIN_NET_DEVICE_FOR_APCLI)
		)
		{
		/*
			APCLI and P2P CLI don't need to do PIN attack checking.
		*/
		return;
	}
	
	/*
		If a static PIN is used, 
		the AP must track multiple failed attempts to authenticate an external Registrar and then enter a lock-down state 
		(This state is signified by setting the attribute AP Setup Locked to TRUE).  
		After at most 10 failed, consecutive attempts, with no time limitation, from any number of external Registrars, 
		the AP shall revert to a locked down state, and the AP shall remain in the locked down state indefinitely 
		(i.e., until the user intervenes to unlock AP's  PIN for use by external Registrars)
	*/
	pWscControl->PinAttackCount++;
	if (pWscControl->PinAttackCount >= pWscControl->MaxPinAttack)
	{		
		pWscControl->bSetupLock = TRUE;
		if (pWscControl->WscSetupLockTimerRunning)
		{
			RTMPCancelTimer(&pWscControl->WscSetupLockTimer, &bCancelled);
			pWscControl->WscSetupLockTimerRunning = FALSE;
		}

		if (pWscControl->PinAttackCount < WSC_LOCK_FOREVER_PIN_ATTACK)
		{
			pWscControl->WscSetupLockTimerRunning = TRUE;
			RTMPSetTimer(&pWscControl->WscSetupLockTimer, pWscControl->SetupLockTime*60*1000);
		}
		pWscControl->PinAttackCount = 0;

			WscBuildBeaconIE(pAd, 
							 pWscControl->WscConfStatus, 
							 FALSE,
							 0,
							 0,
							 (pWscControl->EntryIfIdx & 0xF),
							 NULL,
							 0,
							 AP_MODE);
			WscBuildProbeRespIE(pAd,
								WSC_MSGTYPE_AP_WLAN_MGR,
								pWscControl->WscConfStatus,
								FALSE,
								0,
								0,
								pWscControl->EntryIfIdx,
								NULL,
								0,
								AP_MODE);
		
		APUpdateBeaconFrame(pAd, pWscControl->EntryIfIdx & 0x0F);
	}
}

#endif /* CONFIG_AP_SUPPORT */

/*
	Vendor ID: 0x00372A
	Subelements ID (one-octet)
		Version2				0x00
		AuthorizedMACs			0x01
		Network Key Shareable	0x02
		Request to Enroll		0x03
		Settings Delay Time		0x04
		Reserved for future use	0x05 to 0xFF
	Length (one-octet) - number of octets in the payload of this subelment
		Version2				1B
		AuthorizedMACs			<=30B
		Network Key Shareable	Bool
		Request to Enroll		Bool
		Settings Delay Time		1B
*/
BOOLEAN	WscGenV2Msg(
	IN  PWSC_CTRL		pWpsCtrl,
	IN  BOOLEAN			bSelRegistrar,
	IN	PUCHAR			pAuthorizedMACs,
	IN  INT   			AuthorizedMACsLen,
	OUT	UCHAR			**pOutBuf,
	OUT	INT				*pOutBufLen)
{
	PUCHAR			pWscV2Msg = NULL;
	USHORT			WscV2MsgLen = 0;
	PWSC_REG_DATA	pReg = &pWpsCtrl->RegData;
	INT				templen = 0;
		
	os_alloc_mem(NULL, (UCHAR **)&pWscV2Msg, 128);
	if (pWscV2Msg)
	{
		UCHAR TmpLen = 0;
		pWscV2Msg[0] = 0x00;
		pWscV2Msg[1] = 0x37;
		pWscV2Msg[2] = 0x2A;

		/* Version2 */
		WscAppendV2SubItem(WFA_EXT_ID_VERSION2, &pReg->SelfInfo.Version2, 1, pWscV2Msg+3, &TmpLen);
		WscV2MsgLen += (3 + (USHORT)TmpLen);

		if (bSelRegistrar)
		{
			/* 
				Authorized MACs 
				Registrars (both internal and external) shall add the AuthorizedMACs subelement, 
				this applies to all Configuration Methods, including PIN, PBC and NFC.
			*/
			if (pAuthorizedMACs)
			{
				WscAppendV2SubItem(	WFA_EXT_ID_AUTHORIZEDMACS, 
									pAuthorizedMACs, 
									AuthorizedMACsLen, 
									pWscV2Msg+WscV2MsgLen, 
									&TmpLen	);
				WscV2MsgLen += (USHORT)TmpLen;
			}
			else
			{
				WscAppendV2SubItem(	WFA_EXT_ID_AUTHORIZEDMACS, 
									BROADCAST_ADDR, 
									MAC_ADDR_LEN, 
									pWscV2Msg+WscV2MsgLen, 
									&TmpLen	);
				WscV2MsgLen += (USHORT)TmpLen;
			}
		}

		templen = AppendWSCTLV(WSC_ID_VENDOR_EXT, (*pOutBuf), (UINT8 *)pWscV2Msg, WscV2MsgLen);
		(*pOutBuf) += templen;
		(*pOutBufLen)   += templen;

		os_free_mem(NULL, pWscV2Msg);
		
		return TRUE;
	}
	return FALSE;
}

BOOLEAN	WscAppendV2SubItem(
	IN	UCHAR			SubID,
	IN	PUCHAR			pData,
	IN	UCHAR			DataLen,
	OUT	PUCHAR			pOutBuf,
	OUT	PUCHAR			pOutBufLen)
{
	PUCHAR	pBuf = NULL;
	os_alloc_mem(NULL, &pBuf, DataLen + 10);
	if (pBuf)
	{
		pBuf[0] = SubID;
		pBuf[1] = DataLen;
		NdisMoveMemory(pBuf+2, pData, DataLen);
		*pOutBufLen = (DataLen + 2);
		NdisMoveMemory(pOutBuf, pBuf, *pOutBufLen);
		os_free_mem(NULL, pBuf);
		return TRUE;
	}
	return FALSE;
}

BOOLEAN	WscParseV2SubItem(
	IN	UCHAR			SubID,
	IN	PUCHAR			pData,
	IN	USHORT			DataLen,
	OUT	PUCHAR			pOutBuf,
	OUT	PUCHAR			pOutBufLen)
{
	PEID_STRUCT   pEid;
	USHORT		  Length = 0;
	pEid = (PEID_STRUCT) (pData + 3);
	hex_dump("WscParseV2SubItem - pData", (pData+3), DataLen-3);
	while ((Length + 2 + pEid->Len) <= (DataLen-3))
    {
    	if (pEid->Eid == SubID)
        {
			*pOutBufLen = pEid->Len;
			NdisMoveMemory(pOutBuf, &pEid->Octet[0], pEid->Len);
			return TRUE;
    	}
    	Length = Length + 2 + pEid->Len; 
        pEid = (PEID_STRUCT)((UCHAR*)pEid + 2 + pEid->Len);
	}
	return FALSE;
}

VOID	WscSendEapFragAck(
	IN	PRTMP_ADAPTER		pAdapter,
	IN  PWSC_CTRL			pWscControl,
	IN	PMAC_TABLE_ENTRY	pEntry)
{
	if (pEntry == NULL)
	{
		ASSERT(pEntry!=NULL);
		return;
	}
	if (IS_ENTRY_CLIENT(pEntry))
	{
		pWscControl->bWscLastOne = TRUE;
		if (pAdapter->OpMode == OPMODE_AP)
			WscSendMessage(pAdapter, WSC_OPCODE_FRAG_ACK, NULL, 0, pWscControl, AP_MODE, EAP_CODE_REQ);
		else
		{						
			if (ADHOC_ON(pAdapter) && (pWscControl->WscConfMode == WSC_REGISTRAR))
				WscSendMessage(pAdapter, WSC_OPCODE_FRAG_ACK, NULL, 0, pWscControl, STA_MODE, EAP_CODE_REQ);
			else
				WscSendMessage(pAdapter, WSC_OPCODE_FRAG_ACK, NULL, 0, pWscControl, STA_MODE, EAP_CODE_RSP);
		}
	}
	else if (IS_ENTRY_APCLI(pEntry))
		WscSendMessage(pAdapter, WSC_OPCODE_FRAG_ACK, NULL, 0, pWscControl, AP_CLIENT_MODE, EAP_CODE_RSP);
}

VOID	WscSendEapFragData(
	IN	PRTMP_ADAPTER		pAdapter,
	IN  PWSC_CTRL			pWscControl,
	IN	PMAC_TABLE_ENTRY	pEntry)
{
	INT 	DataLen = 0;
	PUCHAR	pData = NULL;
	
	if (pEntry == NULL)
	{
		ASSERT(pEntry!=NULL);
		return;
	}

	pData = pWscControl->pWscCurBufIdx;
	pWscControl->bWscLastOne = TRUE;
	if (pWscControl->WscTxBufLen > pWscControl->WscFragSize)
	{
		pWscControl->bWscLastOne = FALSE;
		DataLen = pWscControl->WscFragSize;
		pWscControl->WscTxBufLen -= pWscControl->WscFragSize;
		pWscControl->pWscCurBufIdx = (pWscControl->pWscCurBufIdx + pWscControl->WscFragSize);
	}
	else
	{
		DataLen = pWscControl->WscTxBufLen;
		pWscControl->pWscCurBufIdx = NULL;
		pWscControl->WscTxBufLen = 0;
	}
	
#ifdef CONFIG_AP_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_AP(pAdapter)
	{
		if (IS_ENTRY_APCLI(pEntry))
			WscSendMessage(pAdapter, WSC_OPCODE_MSG, pData, DataLen, pWscControl, AP_CLIENT_MODE, EAP_CODE_RSP);
		else
			WscSendMessage(pAdapter, WSC_OPCODE_MSG, pData, DataLen, pWscControl, AP_MODE, EAP_CODE_REQ);
	}
#endif // CONFIG_AP_SUPPORT //

#ifdef CONFIG_STA_SUPPORT
	IF_DEV_CONFIG_OPMODE_ON_STA(pAdapter)
		WscSendMessage(pAdapter, WSC_OPCODE_MSG, pData, DataLen, pWscControl, STA_MODE, EAP_CODE_RSP);
#endif // CONFIG_STA_SUPPORT //
}

#endif /* WSC_V2_SUPPORT */

